package linux.crontab;

import java.util.Calendar;
import linux.crontab.CronMgr.Cron;

/**
在父类的基础上，封装db中cron表的一条记录，并对数据进行处理。
<pre>
CREATE TABLE `cron`(--定时任务配置表
`a` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`stt` tinyint(2) NOT NULL,--状态:0禁用 1启用 9执行完毕
`tC` int(11) NOT NULL,--记录生成时间
`t0` bigint(20),--任务开始时间,null为不限制
`t1` bigint(20),--任务终止时间,null为不限制
`n9` int(11),--任务最多执行次数,null为不限制
`n` int(11) NOT NULL DEFAULT 0,--任务实时执行次数.更新它不用考虑并发,因一个任务同时只有一条线程跑,根据任务id执行sql更新即可:update tb set n=n+1 where a=1
`who` varchar(8) NOT NULL,--谁来执行?
`yy` varchar(128),--年,null为不限制
`MM` varchar(128),--月,null为不限制
`wk` varchar(128),--星期,null为不限制 (*／3为每3天,不过它没有意义,最终存储为null)
`dd` varchar(128),--日,null为不限制
`HH` varchar(128),--时,null为不限制 (null与*等同,与*／1也等同) (“1,2-10/2,13”最终存储为“1 2 4 6 8 10 13”) (“22-6/3,13”最终存储为“22 1 4 13”,排序后为“1 4 13 22”)
`mt` varchar(128),--分,null为不限制 (*／3为每3分钟,最终存储为“*3”)
`ss` varchar(128),--秒,null为不限制
`arg` varchar(128),--执行的额外参数
`bak` varchar(64) NOT NULL --配置的备份,方便db查看,类似yy=* MM=* wk=*／2 dd=1,2 HH=1,2-10/2,13 mt=* ss=*
);
</pre>

@author 834659942@qq.com 2017/05/18
 */
final class CronDb extends Cron {
	private static final int _nMaxRecursion = 32; // 计算的时候，最大的递归次数；用它来控制，不至于程序递归死掉了。
	private final Calendar _calendar;

//`a` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
//`stt` tinyint(2) NOT NULL,--状态:0禁用 1启用 9执行完毕
//`tC` int(11) NOT NULL,--记录生成时间
	private long t0; // bigint(20),--任务开始时间,null为不限制
	private final long t1; // bigint(20),--任务终止时间,null为不限制
	private final int n9; // int(11),--任务最多执行次数,null为不限制
//`n` int(11) NOT NULL DEFAULT 0,--任务实时执行次数.更新它不用考虑并发,因一个任务同时只有一条线程跑,根据任务id执行sql更新即可:update tb set n=n+1 where a=1
//`who` varchar(8) NOT NULL,--谁来执行?
//`yy` varchar(128),--年,null为不限制
	private final Object M; // varchar(128),--月,null为不限制
	private final Object w; // varchar(128),--星期,null为不限制
	private final Object d; // varchar(128),--日,null为不限制
	private final Object H; // varchar(128),--时,null为不限制 (null与*等同,与*/1也等同) (“1,2-10/2,13”最终存储为“1 2 4 6 8 10 13”) (“22-6/3,13”最终存储为“22 1 4 13”,排序后为“1 4 13 22”)
	private final Object m; // varchar(128),--分,null为不限制 (*/3为每3分钟,最终存储为“*3”)
	private final Object s; // varchar(128),--秒,null为不限制
//`arg` varchar(128),--执行的额外参数
//`bak` varchar(64) NOT NULL --配置的备份,方便db查看,类似yy=* MM=* wk=*/2 dd=1,2 HH=1,2-10/2,13 mt=* ss=*

	CronDb(int id, final Object runner, //
			final long t0, final long t1, final int n9, //
			final String MM, final String wk, final String dd, final String HH, final String mm, final String ss) {
		super(id, runner);
		_calendar = Calendar.getInstance();
		this.t0 = t0;
		this.t1 = t1 == 0 ? Long.MAX_VALUE : t1 / 1000 * 1000;
		this.n9 = n9 == 0 ? Integer.MAX_VALUE : n9;
		//this.t0 = System.currentTimeMillis() - t0 > -1000L ? 0 : t0 / 1000 * 1000;
		String[] sArr;
		int[] bArr;
		int i;
		if (MM == null)
			this.M = null;
		else if (MM.charAt(0) == '/') //   /3为：每3个月
			this.M = Integer.valueOf(MM.substring(1));
		else { //   1 3 5为：一月份 三月份 五月份
			sArr = MM.split(" ");
			this.M = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		}
		if (wk == null)
			this.w = null;
		else if (wk.charAt(0) == '/') //   /3为：每3天；不过这没有意义，本来直接抛异常的，后来想想，自己注意就是了，因此忽视它！
			this.w = null;
		else { //   1 3 5为：一月份 三月份 五月份
			sArr = wk.split(" ");
			this.w = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		}
		if (dd == null)
			this.d = null;
		else if (dd.charAt(0) == '/') //   /3为：每3天
			this.d = Integer.valueOf(dd.substring(1));
		else if (dd.indexOf('/') == -1) { //   1 3 5为：一号 三号 五号
			sArr = dd.split(" ");
			this.d = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		} else { //   22 24 26 28-1/2 31 3 4
//		_Log.e(getClass(), 96, TAG, "日期类似22 24 26 28-1/2 31 3 4的，代码中暂不处理。你的日期配置为：" + dd, null);
			sArr = dd.split(" ");
			Object[] arr = new Object[sArr.length + 1];
			this.d = arr;
			for (i = sArr.length - 1; i > -1; i--)
				try {
					arr[i + 1] = Integer.valueOf(sArr[i]);
				} catch (Exception e) {
					arr[i + 1] = sArr[i];
				}
			arr[0] = 1;
		}
		if (HH == null)
			this.H = null;
		else if (HH.charAt(0) == '/') //   /3为：每3小时
			this.H = Integer.valueOf(HH.substring(1));
		else { //   1 3 5为：一点 三点 五点
			sArr = HH.split(" ");
			this.H = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		}
		if (mm == null)
			this.m = null;
		else if (mm.charAt(0) == '/') //   /3为：每3分钟
			this.m = Integer.valueOf(mm.substring(1));
		else { //   1 3 5为：一分 三分 五分
			sArr = mm.split(" ");
			this.m = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		}
		if (ss == null)
			this.s = null;
		else if (ss.charAt(0) == '/') { //   /3为：每3秒钟
			i = Integer.valueOf(ss.substring(1));
			this.s = i == 1 ? null : i * 1000; // 存储为毫秒值，方便计算；"*/1"与null等价
		} else { //   1 3 5为：一秒 三秒 五秒
			sArr = ss.split(" ");
			this.s = bArr = new int[sArr.length];
			for (i = sArr.length - 1; i > -1; i--)
				bArr[i] = Integer.valueOf(sArr[i]);
		}

		if (this.H != null) {
			if (this.m == null && this.s != null)
				throw new Error("在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒");
			if (this.m != null && //
					(this.s != null && this.s instanceof Integer && (int) this.s > 1800000))
				throw new Error("在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒");
		}
	}

	private boolean s(final Calendar calendar, final boolean isMinuteChangedBySelf) {
		if (this.s == null) {
			if (this.m == null && this.H == null && this.d == null && this.w == null && this.M == null) {
				final int calendarMinute = calendar.get(Calendar.MINUTE);
				calendar.setTimeInMillis(calendar.getTimeInMillis() + 1000);
				return calendarMinute == calendar.get(Calendar.MINUTE);
			}
			return false;
		}

		if (this.s instanceof Integer) { // "*/2"   "*/3"
			if (this._tRun == 0)
				return false;
			final long calendarT = calendar.getTimeInMillis();
			final int calendarMinute = calendar.get(Calendar.MINUTE);
			final int confSecond = (int) this.s; //存储毫秒值
			final int moSecond = (int) (calendarT - this._tRun) % confSecond;
			if (moSecond == 0)
				if (isMinuteChangedBySelf)
					return false;
			calendar.setTimeInMillis(calendarT + confSecond - moSecond);
			return calendarMinute == calendar.get(Calendar.MINUTE);
		}

		final int calendarSecond = calendar.get(Calendar.SECOND);
		final int confSecond[] = (int[]) this.s;
		int i = 0, n = 0;
		for (; i < confSecond.length; i++) {
			if ((n = confSecond[i]) < calendarSecond)
				continue;
			if (n > calendarSecond)
				break;
			if (this._tRun == 0 || isMinuteChangedBySelf) // if (n == calendarSecond && this._tRun == 0)
				break;
		}

		if (i != confSecond.length) {
			if (n > calendarSecond)
				calendar.set(Calendar.SECOND, n);
			return false; // 返回假，表示分钟没有被改变
		}

		final long calendarT = calendar.getTimeInMillis();
		calendar.setTimeInMillis(calendarT + (60 + confSecond[0] - calendarSecond) * 1000);
		return true; // 返回真，表示分钟被改变了
	}

	/** 
	 * @param nRecursion 递归次数。代码中要控制递归次数，不要太多，免得程序递归死掉了！
	 * @param isHourChangedBySelf true代表：时钟有变化，且时钟主动变化
	 */
	private boolean m(final Calendar calendar, final int nRecursion, final boolean isHourChangedBySelf) {
		if (nRecursion < 1) {
			// 要打印。。。错误报告。。。
			return false;
		}

		if (this.m == null)
			return false; // 返回假，表示时钟没有被改变

		if (this.m instanceof Integer) { // "*/1"   "*/2"   "*/3"
			if (this._tRun == 0)
				return false;

			final long calendarT = calendar.getTimeInMillis();
			final int confMinute = (int) this.m;
			final int moMinute = (int) ((calendarT / 60000 - this._tRun / 60000) % confMinute); // 取模的值
			if (moMinute == 0)
				if (isHourChangedBySelf || this.s != null)
					return false;

			final int calendarHour = calendar.get(Calendar.HOUR_OF_DAY);
			calendar.add(Calendar.MINUTE, confMinute - moMinute);
			if (this.s != null)
				calendar.set(Calendar.SECOND, 0);
			if (this.s == null || !s(calendar, true))
				return calendarHour != calendar.get(Calendar.HOUR_OF_DAY);
			return m(calendar, nRecursion - 1, false);
		}

		final int calendarMinute = calendar.get(Calendar.MINUTE);
		final int confMinute[] = (int[]) this.m;
		int i = 0, n = 0;
		for (; i < confMinute.length; i++) {
			if ((n = confMinute[i]) < calendarMinute)
				continue;
			if (n > calendarMinute)
				break;
			if (this._tRun == 0 || isHourChangedBySelf || this.s != null)
				return false;
		}

		final int calendarHour = calendar.get(Calendar.HOUR_OF_DAY);
		if (i == confMinute.length) {
			n = confMinute[0];
			calendar.add(Calendar.HOUR_OF_DAY, 1);
		}

		if (n != calendarMinute)
			calendar.set(Calendar.MINUTE, n);
		if (this.s != null)
			calendar.set(Calendar.SECOND, 0);
		if (this.s == null || !s(calendar, true))
			return calendarHour != calendar.get(Calendar.HOUR_OF_DAY);
		return m(calendar, nRecursion - 1, false);
	}

	private boolean H(final Calendar calendar, final int nRecursion) {
		if (nRecursion < 1) {
			// 要打印。。。错误报告。。。
			return false;
		}

		if (this.H == null)
			return false; // 返回假，表示日期没有被改变

		if (this.H instanceof Integer) { // "*/1"   "*/2"   "*/3"
			if (this._tRun == 0)
				return false;

			final long calendarT = calendar.getTimeInMillis();
			final int confHour = (int) this.H;
			final int moHour = (int) ((calendarT / 3600000 - this._tRun / 3600000) % confHour); // 取模的值
			if (moHour == 0 && this.m != null) // 在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒
				return false;

			final int calendarDay = calendar.get(Calendar.DAY_OF_MONTH);
			calendar.add(Calendar.HOUR_OF_DAY, confHour - moHour);
			if (this.m != null) { // 在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒
				calendar.set(Calendar.MINUTE, 0);
				if (this.s != null) {
					calendar.set(Calendar.SECOND, 0);
					s(calendar, true); // 该行代码就是"要求ss值小于每1800秒"的原因，因为这句代码不会导致时钟增加；本来控制在每3600秒内就可以，保险起见，1800可以完全控制。
				}
			}
			if (this.m == null || !m(calendar, nRecursion - 1, true))
				return calendarDay != calendar.get(Calendar.DAY_OF_MONTH);
			return H(calendar, nRecursion - 1);
		}

		final int calendarHour = calendar.get(Calendar.HOUR_OF_DAY);
		final int confHour[] = (int[]) this.H;
		int i = 0, n = 0;
		for (; i < confHour.length; i++) {
			if ((n = confHour[i]) < calendarHour)
				continue;
			if (n > calendarHour)
				break;
			if (this._tRun == 0 || this.m != null) // 在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒
				return false;
		}

		final int calendarDay = calendar.get(Calendar.DAY_OF_MONTH);
		if (i == confHour.length) {
			n = confHour[0];
			calendar.add(Calendar.DAY_OF_MONTH, 1);
		}

		if (n != calendarHour)
			calendar.set(Calendar.HOUR_OF_DAY, n);
		if (this.m != null) { // 在时钟不为null的情况下：若mm为null，要求ss必须为null；若mm不为null，ss也不为null且为每几秒时，要求ss值小于每1800秒
			calendar.set(Calendar.MINUTE, 0);
			if (this.s != null) {
				calendar.set(Calendar.SECOND, 0);
				s(calendar, true); // 该行代码就是"要求ss值小于每1800秒"的原因，因为这句代码不会导致时钟增加；本来控制在每3600秒内就可以，保险起见，1800可以完全控制。
			}
		}
		if (this.m == null || !m(calendar, nRecursion - 1, true))
			return calendarDay != calendar.get(Calendar.DAY_OF_MONTH);
		return H(calendar, nRecursion - 1);
	}

	@Override
	protected boolean calcu(final long now) {
		if (now >= this.t1 || this._nRun >= this.n9)
			return false;

		if (now < this.t0) {
			this._bRun = false; //意思是：排到队列中，但到了时间，却不执行；再calculate()，计算后决定要不要执行
			this._tRun = this.t0;
			return true;
		} else if (this.t0 != 0) {
			this._tRun = this.t0 = 0;
		}

		final Calendar calendar = this._calendar;
		calendar.setTimeInMillis(now / 1000 * 1000);

		s(calendar, false);
		m(calendar, _nMaxRecursion, false);
		H(calendar, _nMaxRecursion);
		System.out.println("\tbRun=" + (calendar.getTimeInMillis() <= this.t1) + ", tRun=" + Test.SDF.format(calendar.getTimeInMillis()));

		return (this._bRun = (this._tRun = calendar.getTimeInMillis()) <= this.t1);

//		final int mt0 = calendar.get(Calendar.MINUTE);
//		final int HH0 = calendar.get(Calendar.HOUR_OF_DAY);
//		final int dd0 = calendar.get(Calendar.DAY_OF_MONTH);
//		final int wk0 = calendar.get(Calendar.DAY_OF_WEEK);
//		final int MM0 = calendar.get(Calendar.MONTH);
//		int a[];
//		int i;
//
//		if (this.s == null) {
//			calendar.add(Calendar.SECOND, 1);
//		} else {
//			if (this.s instanceof Integer) {
//				calendar.add(Calendar.SECOND, (int) this.s);
//			} else {
//				a = (int[]) this.s;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.MINUTE, 1);
//				}
//				calendar.set(Calendar.SECOND, a[i]);
//				a[0] = i + 1;
//			}
//		}
//
//		if (this.m != null) {
//			if (this.m instanceof Integer) {
//				i = calendar.get(Calendar.MINUTE) - mt0;
//				calendar.add(Calendar.MINUTE, (int) this.m - i);
//			} else {
//				a = (int[]) this.m;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.HOUR_OF_DAY, 1);
//				}
//				calendar.set(Calendar.MINUTE, a[i]);
//				a[0] = i + 1;
//			}
//		}
//
//		if (this.H != null) {
//			if (this.H instanceof Integer) {
//				i = calendar.get(Calendar.HOUR_OF_DAY) - HH0;
//				calendar.add(Calendar.HOUR_OF_DAY, (int) this.H - i);
//			} else {
//				a = (int[]) this.H;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.DAY_OF_MONTH, 1);
//				}
//				calendar.set(Calendar.HOUR_OF_DAY, a[i]);
//				a[0] = i + 1;
//			}
//		}
//
//		if (this.d == null && this.w == null && this.M == null)
//			return (_tRun = calendar.getTimeInMillis()) < t1;
//
//		if (this.d != null && this.w == null && this.M == null) {
//			if (this.d instanceof Integer) {
//				i = calendar.get(Calendar.DAY_OF_MONTH) - dd0;
//				calendar.add(Calendar.DAY_OF_MONTH, (int) this.d - i);
//			} else if (this.d instanceof int[]) {
//				a = (int[]) this.d;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.MONTH, 1);
//				}
//				calendar.set(Calendar.DAY_OF_MONTH, a[i]);
//				a[0] = i + 1;
//			} else {
//				_Log.e(getClass(), 232, TAG, "日期类似22 24 26 28-1/2 31 3 4的，代码中暂不处理。你的日期配置为：" + d, null);
//				return false;
//			}
//			return (_tRun = calendar.getTimeInMillis()) < t1;
//		}
//
//		if (this.d == null && this.w != null && this.M == null) {
//			if (this.w instanceof Integer) {
//				i = calendar.get(Calendar.DAY_OF_WEEK) - wk0;
//				calendar.add(Calendar.DAY_OF_WEEK, (int) this.w - i);
//			} else {
//				a = (int[]) this.w;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.DAY_OF_WEEK, 7);
//				}
//				calendar.set(Calendar.DAY_OF_WEEK, a[i]);
//				a[0] = i + 1;
//			}
//			return (_tRun = calendar.getTimeInMillis()) < t1;
//		}
//
//		if (this.d == null && this.w == null && this.M != null) {
//			if (this.M instanceof Integer) {
//				i = calendar.get(Calendar.MONTH) - MM0;
//				calendar.add(Calendar.MONTH, (int) this.M - i);
//			} else {
//				a = (int[]) this.M;
//				if ((i = a[0]) == a.length) {
//					i = 1;
//					calendar.add(Calendar.YEAR, 1);
//				}
//				calendar.set(Calendar.MONTH, a[i]);
//				a[0] = i + 1;
//			}
//			return (_tRun = calendar.getTimeInMillis()) < t1;
//		}
//
//		// -----------------------------------------------------------
//		// -----------------------华丽丽的分隔线-----------------------
//		// -----------------------------------------------------------
//
//		if (this.d != null && this.w == null && this.M != null) {
//			_Log.e(getClass(), 232, TAG, "dd!=null && wk==null && MM!=null这种情况暂不处理！", null);
//		}
//		if (this.d == null && this.w != null && this.M != null) {
//			_Log.e(getClass(), 232, TAG, "dd==null && wk!=null && MM!=null这种情况暂不处理！", null);
//		}
//		if (this.d != null && this.w != null && this.M == null) {
//			_Log.e(getClass(), 232, TAG, "dd!=null && wk!=null && MM==null这种情况暂不处理！", null);
//		}
//		if (this.d != null && this.w != null && this.M != null) {
//			_Log.e(getClass(), 232, TAG, "dd!=null && wk!=null && MM!=null这种情况暂不处理！", null);
//		}
//		return false;
	}

	@Override
	public void run() {
		System.out.println("run()...");
	}

}
